/*
XPTracker - Alternative Agile Tracking Tool
Copyright (C) 2006-2009  Stephen Starkey

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
*/
package org.xptracker.web;

import flexjson.JSONSerializer;
import net.sourceforge.stripes.action.*;
import net.sourceforge.stripes.controller.LifecycleStage;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.lang.StringUtils;
import org.xptracker.domain.EffortEntry;
import org.xptracker.domain.Role;
import org.xptracker.domain.Story;
import org.xptracker.domain.User;
import org.xptracker.persistence.PersistenceManager;

import java.io.StringReader;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

@UrlBinding("/Effort.action")
public class EffortEntryActionBean extends StandardActionBean {
  private JSONSerializer serializer;
  private String storyId;
  private String userId;
  private String comment;
  private String entryId;
  private Story story;
  private Set<User> availableUsers;
  private Set<User> allUsers;
  private String priorComment;

  public String getComment() {
    return comment;
  }

  public void setComment(String comment) {
    this.comment = comment;
  }

  public String getEntryId() {
    return entryId;
  }

  public void setEntryId(String entryId) {
    this.entryId = entryId;
  }

  public String getUserId() {
    return userId;
  }

  public void setUserId(String userId) {
    this.userId = userId;
  }

  public String getStoryId() {
    return storyId;
  }

  public void setStoryId(String storyId) {
    this.storyId = storyId;
  }

  public Story getStory() {
    return story;
  }

  public void setStory(Story story) {
    this.story = story;
  }

  public Set<User> getAvailableUsers() {
    return availableUsers;
  }

  public void setAvailableUsers(Set<User> availableUsers) {
    this.availableUsers = availableUsers;
  }

  public Set<User> getAllUsers() {
    return allUsers;
  }

  public void setAllUsers(Set<User> allUsers) {
    this.allUsers = allUsers;
  }

  public String getPriorComment() {
    return priorComment;
  }

  @DefaultHandler
  public Resolution displayEffortPage() {
    return new ForwardResolution("/editor/effortEntry.jsp");
  }

  @HandlesEvent("Edit")
  public Resolution editEffort() {
    return new ForwardResolution("/editor/effortEntryEdit.jsp");
  }

  @HandlesEvent("Save")
  public Resolution saveEffort() {
    return persistenceManager.inTransaction(new PersistenceManager.UpdateHandler<Resolution>() {
      public Resolution doUpdate(PersistenceManager.UpdateReceiver receiver) {
        Story storyToUpdate = loadEntity(Story.class, storyId);
        for(EffortEntry entry: storyToUpdate.getEffortEntries()) {
          receiver.remove(entry);
        }

        storyToUpdate.getEffortEntries().clear();
        for (EffortEntry entry : getStory().getEffortEntries()) {
          Date start = entry.getStart();
          if (start != null) {
            User user = domain.loadEntity(User.class, entry.getUserId());
            EffortEntry realEntry = new EffortEntry();
            realEntry.setUser(user);
            realEntry.setStory(storyToUpdate);
            realEntry.setStart(start);
            realEntry.setComment(entry.getComment());
            Date end = entry.getEnd();
            if (end != null && (end.after(start) || end.equals(start))) {
              realEntry.setEnd(end);
            } else if (end != null) {
              getContext().getMessages().add(new SimpleMessage("End date of ''{0}'' ignored.", end));
            }
            receiver.insert(realEntry);
            storyToUpdate.getEffortEntries().add(realEntry);
          }
        }

        receiver.update(storyToUpdate);
        getContext().getMessages().add(new SimpleMessage("Effort data saved."));
        return new RedirectResolution(EffortEntryActionBean.class).addParameter("storyId", storyId);
      }
    });
  }

  @HandlesEvent("StartClock")
  public Resolution startClock() {
    return persistenceManager.inTransaction(new PersistenceManager.UpdateHandler<Resolution>() {
      public Resolution doUpdate(PersistenceManager.UpdateReceiver receiver) {
        Story story = loadEntity(Story.class, storyId);
        User user = loadEntity(User.class, userId);
        EffortEntry entry = new EffortEntry(story, user, new Date(), null, comment);
        receiver.insert(entry);
        story.getEffortEntries().add(entry);
        receiver.update(story);
        StringBuilder functionBuilder = new StringBuilder();
        functionBuilder.append(functionCall("swapAction",
            "stopClock(" + entry.getUser().getId() + ", " + entry.getId() + ")", "Stop", entry.getUser())).append('\n');
        return new StreamingResolution("text/plain", new StringReader(functionBuilder.toString()));
      }
    });
  }

  @HandlesEvent("StopClock")
  public Resolution stopClock() {
    return persistenceManager.inTransaction(new PersistenceManager.UpdateHandler<Resolution>() {
      public Resolution doUpdate(PersistenceManager.UpdateReceiver receiver) {
        EffortEntry entry = loadEntity(EffortEntry.class, entryId);
        entry.stop();
        entry.setComment(comment);
        receiver.update(entry);
        receiver.update(entry.getStory()); // This causes the story's ObjectChange to get registered.
        DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        StringBuilder functionBuilder = new StringBuilder();
        functionBuilder.append(functionCall("swapAction",
            "startClock(" + entry.getUser().getId() + ")", "Start", entry.getUser())).append('\n');
        functionBuilder.append(functionCall("addEntry", entry.getId(), entry.getUser(),
            fmt.format(entry.getStart()), fmt.format(entry.getEnd()), entry.getComment()));
        return new StreamingResolution("text/plain", new StringReader(functionBuilder.toString()));
      }
    });
  }

  private String functionCall(String functionName, Object... params) {
    List<String> paramResult = new ArrayList<String>();
    for(Object o: params) {
      paramResult.add(serializer.serialize(o));
    }
    return new StringBuilder().append(functionName).append("(").append(StringUtils.join(paramResult.iterator(), ", ")).
        append(");").toString();
  }

  @Before(stages=LifecycleStage.BindingAndValidation)
  public void setup() {
    serializer = new JSONSerializer();
    //noinspection deprecation
    this.story = domain.fetchStory(param("storyId"));
    //noinspection unchecked
    allUsers = new TreeSet<User>(new BeanComparator("username"));
    allUsers.addAll(domain.listUsers());

    //noinspection unchecked
    availableUsers = new TreeSet<User>(new BeanComparator("username"));

    for(User tracker: story.getTrackers()) {
      availableUsers.add(tracker);
    }
    
    for(EffortEntry entry: story.getEffortEntries()) {
      if (!entry.isCompleted()) {
        availableUsers.remove(entry.getUser());
        priorComment = entry.getComment();
      } else {
        availableUsers.add(entry.getUser());
      }
    }

    Iterator<User> availableUsersI = availableUsers.iterator();
    while (availableUsersI.hasNext()) {
      User u = availableUsersI.next();
      if (!u.hasRole(Role.editor)) {
        availableUsersI.remove();
      }
    }
  }
}
